#!/usr/bin/env groovy
// @file     BarcodeProcessor
// @purpose  barcode processor
// @author   Mac Radigan  
// @version $Id$

package ycoa.scan.barcode

import org.codehaus.groovy.scriptom.*
import au.com.bytecode.opencsv.*
import java.text.*
import java.util.TimeZone
import org.apache.commons.io.FileUtils

import ycoa.xml.*
import ycoa.service.*
import ycoa.session.*
import ycoa.markup.*
import ycoa.xml.*
import ycoa.scan.process.*
import ycoa.scan.barcode.*
import ycoa.graphics.*
import ycoa.archive.*
import ycoa.configuration.YCOAConfiguration
import sys.utilities.Encoder
import ycoa.scan.barcode.BarcodeProcessor
import ycoa.scan.barcode.BarcodeParser

import org.apache.log4j.Logger

public class GravicBarcodeProcessor extends BarcodeProcessor {

  static Logger log = Logger.getLogger(GravicBarcodeProcessor.class)

  private String message
  private AntBuilder ant = new AntBuilder()
  private String host
  private String port
  private String username
  private String password
  private String referenceFile
  private String logsBase
  private String inputRoot
  private String outputRoot
  private String logRoot
  private String outputFile
  private String market
  private String sourceRoot
  private String archiveRoot
  private String archiveFormat
  private long epoch = System.currentTimeMillis()/1000

  GravicBarcodeProcessor() {
    configure("test")
  }

  GravicBarcodeProcessor(String market) {
    configure(market)
  }

  public void configure(market) {
    message = "configuring Gravic"
    // CONFIGURATION SETTINGS
    def config = YCOAConfiguration.getConfiguration()
    log.debug(message)
    //   host
    host = config.server[market.toLowerCase()].host
    port = config.server[market.toLowerCase()].port
    //   account
    def encoder = new Encoder("rot13")
    username = encoder.decode(config.modules.scan.account.username)
    password = encoder.decode(config.modules.scan.account.password)
    //   forms
    referenceFile = config.modules.scan.forms.barcode.reference
    logsBase = config.modules.scan.forms.barcode.logs
    //   processing
    inputRoot = config.modules.scan.processing.datain
    logRoot = config.modules.scan.processing.logs
    outputRoot = config.modules.scan.processing.dataout
    //   intermediate file
    outputFile = "${outputRoot}${File.separator}output.xml"
    // SELECT MARKET
    this.market = market
    sourceRoot = null
    archiveRoot = "${logsBase}${File.separator}${market.toUpperCase()}"
    archiveFormat = "${archiveRoot}\\CP_[ClassReportID]\\Barcode_CP_[ClassReportID].tif"
    sourceRoot = config.modules.scan.forms.barcode.sources[market.toLowerCase()]
    message = "Gravic configuration complete"
  }

  // PROCESS IMAGES
  //   1.  Process images using Gravic with package and deployment features
  public void allInOne() {
    message = "process starting..."
    log.debug("all-in-one process starting")
    try {
      log.info("reading TIF files from directory:")
      log.info(" dir: ${sourceRoot}")
      new File(sourceRoot).eachFileMatch(~/.*\.tif/) { scannedFile ->
        // delete files from processing directory
        log.info("cleaning processing directories:")
        log.info(" dir: ${inputRoot}")
        log.info(" dir: ${outputRoot}")
        ant.delete() { 
          fileset( dir:inputRoot ) { include( name:"*" ) }
          fileset( dir:outputRoot ) { include( name:"*" ) }
        }
        try {
          log.info("=> processing ${scannedFile.getAbsolutePath()}")
          // move files for processing
          ant.move( todir:inputRoot ) { 
           //fileset( dir:sourceRoot ) { include( name:"*.tif" ) }
           fileset( dir:sourceRoot ) { include( name:scannedFile.getName() ) }
          }
          // rotate images 90 degrees
          log.info("creating new image magick")
          def convert = new ImageMagick()
          new File(inputRoot).eachFileMatch(~/.*\.tif/) { file ->
            log.info("rotating file (90 deg CW):  ${file}")
            convert.rotate(file, file, 90)
          } 
          // process images
          log.info("creating new image magick")
          def firstFile = new File(inputRoot).listFiles().toList()[0]
          def scanStation = new GravicScanStation()
          if( null != firstFile ) {
            scanStation.process(referenceFile, firstFile, outputFile, archivePattern)
            // if the TIF is not oriented correctly, rotate 180 degrees
            if( (new File(outputFile)).exists() ) {
              def barcodeParser = new GravicBarcodeProcessor()
              def barcodeRecordset = barcodeParser.parseOutput(outputFile)
              if(0==barcodeRecordset.records[0].classReportID) {
                new File(inputRoot).eachFileMatch(~/.*\.tif/) { file ->
                  log.info("rotating file (180 deg CW):  ${file}")
                  convert.rotate(file, file, 180)
                }
                scanStation.process(referenceFile, firstFile, outputFile, archivePattern)
              }
            }
          }
          // copy files after processing
          if(false) {
            ant.copy( todir:logRoot ) { 
              fileset( dir:outputRoot ) { include( name:"*.xml" ) }
              fileset( dir:inputRoot ) { include( name:"*.tif" ) }
            }
          }
          // move files after processing
          log.info("moving files after processing")
          ant.move( todir:outputRoot ) { 
            fileset( dir:inputRoot ) { include( name:"*.tif" ) }
          }
          // rotate images -90 degrees
          log.info("rotating images -90 degrees")
          new File(inputRoot).eachFileMatch(~/.*\.tif/) { file ->
           println "rotating file (90 deg CCW):  ${file}"
           convert.rotate(file, file, -90)
          } 
          // complete processing
          parseResults()
          uploadResults()
          archiveFiles()
          // delete files after processing
          log.info("cleaning up")
          ant.delete() { 
            fileset( dir:inputRoot ) { include( name:"*" ) }
            fileset( dir:outputRoot ) { include( name:"*" ) }
          }
          message = """<font color="blue">complete</font>"""
        } catch(e) {
	  log.error("ERROR:  ${e.getMessage()}")
          message =  """<font color="red">error: ${e.getMessage()}</font>"""
          //throw new Exception("Error processing images.", e)
        }
      }
    } catch(e) {
      //throw new Exception("Error processing images.", e)
      log.error("ERROR:  ${e.getMessage()}")
      message =  """<font color="red">error: ${e.getMessage()}</font>"""
    }
  }

  // PROCESS IMAGES
  //   1.  Process images using Gravic with package and deployment features
  public void processImages() {
    log.debug("processing images")
    // delete files from processing directory
    ant.delete() { 
      fileset( dir:inputRoot ) { include( name:"*" ) }
      fileset( dir:outputRoot ) { include( name:"*" ) }
    }
    new File(sourceRoot).eachFileMatch(~/.*\.tif/) { scannedFile ->
      try {
        // move files for processing
        ant.move( todir:inputRoot ) { 
         //fileset( dir:sourceRoot ) { include( name:"*.tif" ) }
         fileset( dir:sourceRoot ) { include( name:scannedFile.getName() ) }
        }
        // rotate images 90 degrees
        def convert = new ImageMagick()
        new File(inputRoot).eachFileMatch(~/.*\.tif/) { file ->
         log.info("rotating file:  ${file}")
         convert.rotate(file, file, 90)
        } 
        // process images
        def firstFile = new File(inputRoot).listFiles().toList()[0]
        if( null != firstFile ) {
          def scanStation = new GravicScanStation()
          scanStation.process(referenceFile, firstFile, outputFile, archivePattern)
        }
        // copy files after processing
        ant.copy( todir:logRoot ) { 
          fileset( dir:outputRoot ) { include( name:"*.xml" ) }
          fileset( dir:inputRoot ) { include( name:"*.tif" ) }
        }
        // move files after processing
        //ant.move( todir:outputRoot ) { 
        //  fileset( dir:inputRoot ) { include( name:"*.tif" ) }
        //}
        // delete files after processing
        ant.delete() { 
          fileset( dir:inputRoot ) { include( name:"*" ) }
          //fileset( dir:outputRoot ) { include( name:"*" ) }
        }
        message = """<font color="blue">complete</font>"""
      } catch(e) {
        message =  """<font color="red">error</font>"""
	log.error("${e.getMessage()}")
        throw new Exception("Error processing images.", e)
      }
    }
  }

  // PARSE RESULTS
  public void parseResults() {
    try {
      log.debug("parsing results")
      log.info("list file: ${outputFile}")
      if( (new File(outputFile)).exists() ) {
        def barcodeParser = new BarcodeParser()
        def barcodeRecordset = barcodeParser.parseOutput(outputFile)
        barcodeParser.printFormattedRecordset(barcodeRecordset)
      }
      message = """<font color="blue">complete</font>"""
    } catch(e) {
      e.printStackTrace()
      message = """<font color="red">error</font>"""
      log.error("${e.getMessage()}")
      throw new Exception("Error parsing results.", e)
    }
  }

  // ARCHIVE FILES
  public void archiveFiles() {
    try {
      log.debug("archive files")
      if( (new File(outputFile)).exists() ) {
        def fileMap = [:]
        def barcodeParser = new BarcodeParser()
        def barcodeRecordset = barcodeParser.parseOutput(outputFile)
        def logFile = null
        def archive = new Archive(archiveRoot, "Barcode")
        def classReportID = 0
        def studentID = 0
        def init = false
        barcodeParser.printFormattedRecordset(barcodeRecordset)
        barcodeRecordset.records.each() { record ->
          log.debug("processing record ${record.classReportID}")
          //if( !fileMap.containsKey(record.formName) ) {
          //  fileMap.put( "${outputRoot}/${record.formName}", record )
          //}
          logFile = archive.getLogFile("CP", "${record.classReportID}", "LOG")
          FileUtils.touch(logFile)
          if(!init) { 
            logFile.append( record.getLogHeader() ) 
            studentID = record.studentID
          }
          logFile.append( record.getLogString() )
          classReportID = record.classReportID
          init = true
        }
        //def xmlFile = archive.getLogFilename("CP", "${classReportID}", "${studentID}", "xml")
        //ant.copy( toFile:"${xmlFile}", file:"${outputFile}") 
        //def tifFile = archive.getLogFilename("CP", "${classReportID}", "${studentID}", "tif")
        //ant.copy( toFile:"${tifFile}", file:"${scannedFile.getAbsolutePath()}") 
        log.debug("moving XML files")
        archive.move( outputFile, "CP", classReportID, studentID, "xml")
        new File(outputRoot).eachFileMatch(~/.*\.tif/) { scannedFile ->
          archive.move( scannedFile.getAbsolutePath(), "CP", classReportID, studentID, "tif")
        }
        //fileMap.each() { source, record ->
        //  // do nothing
        //}
      }
    } catch(e) {
      e.printStackTrace()
      message = """<font color="red">archival failed</font>"""
      log.error("${e.getMessage()}")
      throw new Exception("Unable to archive files.", e)
    }
  }

  // CALL WEBSERVICE
  // 1.  Upload results from CSV file in output root to database.
  // 2.  Move files from the output directory to the logs.
  // 3.  Call file archiver
  public void uploadResults() {
    try {
      log.debug("uploading results")
      if( (new File(outputFile)).exists() ) {
        log.debug("creating barcode parser...")
        def barcodeParser = new BarcodeParser()
        log.debug("parsing output...")
        def barcodeRecordset = barcodeParser.parseOutput(outputFile)
        log.debug("creating session manager...")
        def manager = new SessionManager(market)
        log.debug("setting credentials...")
        manager.setCredentials(username, password)
        log.debug("establishing service...")
        def service = manager.getServiceClient()
        log.debug("logging in...")
        if(service.login()) {
          // logged in
        } else {
          message = """<font color="red">authorization failed</font>"""
          return
        }
        def url = null
        barcodeRecordset.records.each() { record ->
          url = record.getURL()
          //println "calling service: ${url}"
          def data = null
          try {
            data = service.webservice(url)
            //println "RESULT:  ${data}"
          } catch(e) {
            message = """<font color="red">webservice failed</font>"""
            log.error("${e.getMessage()}")
            //println e.getMessage()
            throw new Exception("Unable to upload results.", e)
          }
        }
        message = """<font color="blue">complete</font>"""
      }
    } catch(e) {
      message = """<font color="red">webservice failed</font>"""
      log.error("${e.getMessage()}")
      //println e.getMessage()
      //message = e.getMessage()
      throw new Exception("Unable to upload results.", e)
    }
  }

  // GET WEB RESPONSE
  public String getMessage() {
    return message
  }

}  // end of class


// *EOF*
